package emulator.hardware.io.peripherals.iec.sim;

import emulator.hardware.io.peripherals.IecPort;

public class IecWorld
{
	public Condition ATN = new Condition() { @Override public boolean evaluate() { return !getAtnIn(); } };
	public Condition CLK = new Condition() { @Override public boolean evaluate() { return !getClkIn(); } };  
	public Condition DATA = new Condition() { @Override public boolean evaluate() { return !getDataIn(); } };  
	public Condition nATN = new Condition() { @Override public boolean evaluate() { return getAtnIn(); } };
	public Condition nCLK = new Condition() { @Override public boolean evaluate() { return getClkIn(); } };  
	public Condition nDATA = new Condition() { @Override public boolean evaluate() { return getDataIn(); } };
	public Condition TALK = new Condition() { @Override public boolean evaluate() { return _doSend; } };
	public Condition nTALK = new Condition() { @Override public boolean evaluate() { return !_doSend; } };
	public Condition LISTEN = new Condition() { @Override public boolean evaluate() { return _doReceive; } };
	public Condition nLISTEN = new Condition() { @Override public boolean evaluate() { return !_doReceive; } };
	public Condition TIMEOUT = new Condition() { @Override public boolean evaluate() { return _timer == 0; } };
	public Condition nTIMEOUT = new Condition() { @Override public boolean evaluate() { return _timer > 0; } };
	public Condition EOI = new Condition() { @Override public boolean evaluate() { return _isEoi; } };
	public Condition nEOI = new Condition() { @Override public boolean evaluate() { return !_isEoi; } };
	
	public Effect PULL_CLK = new Effect() { @Override public void execute() { _port.setBusClk(false); } };
	public Effect RELEASE_CLK = new Effect() { @Override public void execute() { _port.setBusClk(true); } };
	public Effect PULL_DATA = new Effect() { @Override public void execute() { _port.setBusData(false); } };
	public Effect RELEASE_DATA = new Effect() { @Override public void execute() { _port.setBusData(true); } };
	public Effect BEGIN_SEND = new Effect() { @Override public void execute() { _doSend = true; } };
	public Effect END_SEND = new Effect() { @Override public void execute() { _doSend = false; } };
	public Effect BEGIN_RECEIVE = new Effect() { @Override public void execute() { _doReceive = true; } };
	public Effect END_RECEIVE = new Effect() { @Override public void execute() { _doReceive = false; } };
	public Effect SET_TIMEOUT(int timerValue) { return new SetTimer(timerValue); }
	public Effect RCV_BIT(int index) { return new Receiver(index); }
	public Effect SND_BIT(int index) { return new Sender(index); }
	public Effect SET_EOI(boolean value) { return new SetEoi(value); }

	private class SetTimer implements Effect
	{
		private int _timerValue;
		SetTimer(int timerValue) { _timerValue = timerValue; }
		@Override public void execute() { _timer = _timerValue; }
	}

	private class SetEoi implements Effect
	{
		private boolean _eoiValue;
		SetEoi(boolean eoiValue) { _eoiValue = eoiValue; }
		@Override public void execute() { _isEoi = _eoiValue; }
	}
	
	private class Receiver implements Effect
	{
		private int _index;
		
		Receiver(int index) { _index = index; }
		
		@Override
		public void execute()
		{
			if (_index == 0)
				_data = 0;
			if (_port.getData() == true)
				_data |= 1 << _index;
		}
	}
	
	private class Sender implements Effect
	{
		private int _index;
		
		Sender(int index) { _index = index; }
		
		@Override
		public void execute()
		{
			_port.setBusData((_data & (1 << _index)) > 0);
		}
	}
	public static Event TRUE = new Event(null) { @Override public boolean evaluate() { return true; } };
	
	public IecWorld(IecPort port)
	{
		_port = port;
		reset();
	}
	
	public void reset()
	{
		_doSend = false;
		_doReceive = false;
		_timer = 0;
	}
	
	public void tick()
	{
		if (_timer > 0)
			--_timer;
		atnIn = _port.getAtn();
		clkIn = _port.getClk();
		dataIn = _port.getData();
	}

	private IecPort _port;
	private boolean _doSend;
	private boolean _doReceive;
	private boolean _isEoi;
	private int _timer;
	private int _data;
	private boolean atnIn;
	private boolean clkIn;
	private boolean dataIn;

	public void setDataByte(int data) { _data = data; }
	public int getDataByte() { return _data; }
	
	public boolean getAtnIn() {
		return atnIn;
	}
	public boolean getClkIn() {
		return clkIn;
	}
	public boolean getDataIn() {
		return dataIn;
	}
}
